package bbcache

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/go-redis/redis"
	"github.com/olivere/elastic/v7"
	"github.com/spf113/cast"
	global2 "longmen/server/config/global"
	"longmen/server/internal/rpc/tencentim/imsdk"
	messages2 "longmen/server/internal/rpc/tencentim/messages"
	"longmen/server/internal/rpc/tencentim/messages/sports"
	"longmen/server/pkg/common/helper/const"
	"longmen/server/pkg/common/helper/util"
	"sync"
)

type (
	Score struct {
		MatchId        int   `json:"match_id"`         // 比赛id
		MatchStatus    int   `json:"match_status"`     // 比赛状态，详见状态码->比赛状态
		TimeLeft       int   `json:"time_left"`        // 小节剩余时间(秒)
		AwayScore      []int `json:"away_score"`       // 客队比分
		HomeScore      []int `json:"home_score"`       // 主队比分 [23, 32, 22, 34, 0 ], 数组下标 （0 第一节比分 1 第二节比分，2 第三节比分，3 //第4节分数 4 //加时分数）
		AwayTotalScore int   `json:"away_total_score"` // 客队总得分
		HomeTotalScore int   `json:"home_total_score"` // 主队总得分

	}
	PublishScoreInterface interface {
		GetScore() Score
	}
	BasketballScoreResponse struct {
		Type             string `json:"type"`
		BasketballScores Score  `json:"basketball_scores"`
	}
	ScoreCache struct {
		rds redis.UniversalClient
		es  *elastic.Client
	}
)

var scoreInstance *ScoreCache
var scoreOnce sync.Once

func NewScoreCache() *ScoreCache {
	scoreOnce.Do(func() {
		scoreInstance = &ScoreCache{
			rds: global2.GetRds(),
			es:  global2.GetEs(),
		}
	})
	return scoreInstance
}

// 篮球比分key
func (t *ScoreCache) genScoreKey(matchId int) string {
	return fmt.Sprintf("match_live:basketball_score:%d", matchId)
}

// PublishLiveScore 写入篮球比分
func (t *ScoreCache) PublishLiveScore(records []PublishScoreInterface) error {
	data := make([]Score, 0)
	pp := t.rds.Pipeline()
	for _, record := range records {
		scores := record.GetScore()
		data = append(data, scores)
		msg := sports.BasketballScoreMessage{
			SentMessageStruct: messages2.SentMessageStruct{Type: messages2.ScoreLiveMessageType},
			Scores:            scores,
		}
		marshal, _ := json.Marshal(msg)

		err := pp.Set(t.genScoreKey(scores.MatchId), string(marshal), CacheExpire).Err()
		if err != nil {
			global2.Log.Error("set cache error: ", err)
		}
		imsdk.GetSdk().BroadcastInterface(scores.MatchId, msg, 0)
	}
	_, err := pp.Exec()
	if err != nil {
		global2.Log.Errorf("Pipeline run error: %v", err)
	}
	t.writeToes(data)
	return nil
}

func (t *ScoreCache) LiveScore(records []Score) error {
	pp := t.rds.Pipeline()
	for _, record := range records {
		msg := sports.BasketballScoreMessage{
			SentMessageStruct: messages2.SentMessageStruct{Type: messages2.ScoreLiveMessageType},
			Scores:            record,
		}
		marshal, _ := json.Marshal(msg)

		err := pp.Set(t.genScoreKey(record.MatchId), string(marshal), CacheExpire).Err()
		if err != nil {
			global2.Log.Error("set cache error: ", err)
		}
	}
	_, err := pp.Exec()
	if err != nil {
		global2.Log.Errorf("Pipeline run error: %v", err)
	}
	t.writeToes(records)
	return nil
}

// GetScoreCache 获取最新的篮球比分
func (t *ScoreCache) GetScoreCache(matchId int) string {
	result, err := t.rds.Get(t.genScoreKey(matchId)).Result()
	if err != nil {
		return ""
	}
	return result
}

// 篮球比分KEY
func (t *ScoreCache) getEsIndexName() string {
	return _const.ES_MATCH_BASKETBALL_SCORE
}

func (t *ScoreCache) InitEsIndex() {
	t.es.Index().Index(t.getEsIndexName())
}

func (t *ScoreCache) writeToes(sendData []Score) {
	bulks := make([]*elastic.BulkIndexRequest, 0)
	total := 3000 // 3000条批量写一次
	writeCount := 0
	for _, datum := range sendData {
		// 赛程ID
		id := fmt.Sprintf("%d", datum.MatchId)
		bulks = append(bulks, elastic.NewBulkIndexRequest().Index(t.getEsIndexName()).Doc(datum).Id(id))
		writeCount++
		if writeCount == total {
			t.bulkToEs(bulks)
			bulks = make([]*elastic.BulkIndexRequest, 0)
		}
	}

	if len(bulks) > 0 {
		t.bulkToEs(bulks)
	}

}

func (t *ScoreCache) bulkToEs(records []*elastic.BulkIndexRequest) {
	bulk := t.es.Bulk()
	for i := range records {
		bulk.Add(records[i])
	}
	res, err := bulk.Do(context.Background())
	if err != nil {
		global2.Log.Errorf("exec bulk to es : %v", err)
		return
	}
	for _, item := range res.Failed() {
		global2.Log.Errorf("write to es err: %s", item.Error.Reason)
	}
}

func (t *ScoreCache) GetScoreRedisCache(matchIds []interface{}) []*Score {
	arrays := make([]string, len(matchIds))
	for i, v := range matchIds {
		arrays[i] = t.genScoreKey(cast.ToInt(v))
	}
	ballMatchRedis := make([]*Score, 0)
	result, _ := t.rds.MGet(arrays...).Result()
	for _, val := range result {
		if v, ok := val.(string); ok {
			var resp BasketballScoreResponse
			if util.Json2Struct(v, &resp) == nil {
				ballMatchRedis = append(ballMatchRedis, &resp.BasketballScores)
			}
		}
	}
	return ballMatchRedis
}
